import { DbClientContract } from '@zenstackhq/runtime';
import { HttpException, Inject, Injectable, Scope } from "@nestjs/common";
import { HttpAdapterHost, REQUEST } from "@nestjs/core";
import { loadAssets } from "../shared";
import { RPCApiHandler } from '../api/rpc';
import { ENHANCED_PRISMA } from "./zenstack.constants";
import { ApiHandlerOptions } from './interfaces';

/**
 * The ZenStack API handler service for NestJS. The service is used to handle API requests
 * and forward them to the ZenStack API handler. It is platform agnostic and can be used
 * with any HTTP adapter.
 */
@Injectable({ scope: Scope.REQUEST })
export class ApiHandlerService {
    constructor(
        private readonly httpAdapterHost: HttpAdapterHost,
        @Inject(ENHANCED_PRISMA) private readonly prisma: DbClientContract,
        @Inject(REQUEST) private readonly request: unknown
    ) { }

    async handleRequest(options?: ApiHandlerOptions): Promise<unknown> {
        const { modelMeta, zodSchemas } = loadAssets(options || {});
        const requestHandler = options?.handler || RPCApiHandler();
        const hostname = this.httpAdapterHost.httpAdapter.getRequestHostname(this.request);
        const requestUrl = this.httpAdapterHost.httpAdapter.getRequestUrl(this.request);
        // prefix with http:// to make a valid url accepted by URL constructor
        const url = new URL(`http://${hostname}${requestUrl}`);
        const method = this.httpAdapterHost.httpAdapter.getRequestMethod(this.request);
        const path = options?.baseUrl && url.pathname.startsWith(options.baseUrl) ? url.pathname.slice(options.baseUrl.length) : url.pathname;
        const searchParams = url.searchParams;
        const query = Object.fromEntries(searchParams);
        const requestBody = (this.request as { body: unknown }).body;

        const response = await requestHandler({
            method,
            path,
            query,
            requestBody,
            prisma: this.prisma,
            modelMeta,
            zodSchemas,
            logger: options?.logger,
        });

        // handle handler error
        // if response code >= 400 throw nestjs HttpException
        // the error response will be generated by nestjs
        // caller can use try/catch to deal with this manually also
        if (response.status >= 400) {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            throw new HttpException(response.body as Record<string, any>, response.status)
        }
        return response.body
    }
}
